package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

//在消费者端进行激活此拦截器org.apache.dubbo.rpc.Filter
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter,Runnable {

    //存储方法执行得相关信息（方法名称，方法执行耗时，方法执行结束时间）
    Map<String, List<MethodInfo>> methodTimes = new ConcurrentHashMap<>();

    //每隔5s调用一次下方的run方法，统计计算一次1分钟内每个方法的TP90、TP99的耗时情况
    public TPMonitorFilter(){
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this,5,5, TimeUnit.SECONDS);
    }

    /**
     * 线程执行方法,每隔5s计算并打印最近1分钟内每个方法的TP90、TP99的耗时情况
     */
    @Override
    public void run() {
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String dateStr = dateFormat.format(date);
        //循环打印TP90、TP99的耗时情况
        for(Map.Entry<String,List<MethodInfo>> methodInfos : methodTimes.entrySet()){
            System.out.println(dateStr +"-->"+ methodInfos.getKey()+"TP90:"+ getTP(methodInfos.getValue(),0.9)+"ms   ,"
                                +"TP99:"+ getTP(methodInfos.getValue(),0.99)+"ms");
        }
    }

    /**
     * 后续会每隔5s调用一次此方法，计算并打印最近1分钟内每个方法的TP90、TP99的耗时情况
     * @param methodInfos
     * @param rate
     * @return
     */
    private long getTP(List<MethodInfo> methodInfos, double rate) {
        // 构建一个临时集合保存 用于满足1一分钟之内的数据
        List<MethodInfo> sortInfo = new ArrayList<>();
        // 计算最近一分钟的TP90 和 TP99
        long endTime = System.currentTimeMillis();
        long startTime = System.currentTimeMillis()-60000;

        for (int i = 0; i < methodInfos.size(); i++) {
            MethodInfo methodInfo = methodInfos.get(i);
            //将满足条件的方法信息存储到临时集合中
            if(methodInfo.getEndTimes() >= startTime && methodInfo.getEndTimes() <= endTime){
                sortInfo.add(methodInfo);
            }
        }
        //对满足1一分钟之内的数据进行排序
        sortInfo.sort(new Comparator<MethodInfo>() {
            @Override
            public int compare(MethodInfo o1, MethodInfo o2) {
                if(o1.getTimes() > o2.getTimes()){
                    return 1;
                }else if(o1.getTimes() < o2.getTimes()){
                    return -1;
                }else {
                    return 0;
                }
            }
        });
        //获取当前排序后集合中的指定百分比数值的位置，此位置存储的数据就是当前计算的tp90/tp99
        int index = (int) (sortInfo.size() * rate);
        return sortInfo.get(index).getTimes();
    }

    /**
     * 方法执行就拦截：利用TPMonitorFilter在消费端记录每个方法的请求耗时时间
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 获取执行方法名
        Result result = null;
        Long takeTime = 0L;
        try{
            //获取方法执行的起始时间
            long startTimes = System.currentTimeMillis();
            //执行方法
            result = invoker.invoke(invocation);
            //获取方法执行的结束时间
            long endTimes = System.currentTimeMillis();
            //方法耗时
            takeTime = endTimes - startTimes;

        }catch (Exception e){
            e.printStackTrace();
            return result;
        }
//        System.out.println("method="+invocation.getMethodName()+"耗时时间为: "+takeTime+"ms");
        String methodName = invocation.getMethodName();
        List<MethodInfo> methodInfos = methodTimes.get(methodName);
        //方法执行第一次的时候，创建空集合进行存储
        if(methodInfos == null){
            methodInfos = new ArrayList<>();
            methodTimes.put(methodName,methodInfos);
        }
        //将当前的方法添加到map中指定的记录当前方法执行情况的集合中
        methodInfos.add(new MethodInfo(invocation.getMethodName(),takeTime,System.currentTimeMillis()));

        return result;
    }

}
